# [七] Bean的实例化-循环依赖

循环依赖

如果存在A中有B属性,B中有A属性,那么当依赖注入的时候,就会产生当A还没创建完的时候,由于对B的创建再次返回创建A,最终造成循环依赖。

  • Spring 中的循环依赖只会出现在单例实例无参构造函数实例化情况下。
  • 原型模式下以及有参构造器注入的循环依赖Spring 无法解决,都会直接报错。

# 循环依赖的业务场景

以下代码就会产生循环依赖的问题,那么Spring 是如何解决的? 类:CircularRefA

@Service
public class CircularRefA {
    CircularRefA(){
        System.out.println("Instance of CircularRefA ======");
    }
    @Autowired
    private CircularRefB circularRefB;
}

类:CircularRefB

@Service
public class CircularRefB {
    CircularRefB(){
        System.out.println("Instance of CircularRefB  ======");
    }
    @Autowired
    private CircularRefA circularRefA;
}

# 循环依赖的相关方法

单例模式下的循环依赖流程

  • ├─ getBean ① 实例化入口
  • │ ├─ doGetBean
  • │ │ ├─ getSingleton ② 查看缓存里是否有实例
  • │ │ ├─ getObjectForBeanInstance ③ 如果缓存有实例,直接返回实例
  • │ │ ├─ getSingletond ④ 缓存没有实例,创建实例
  • │ │ │ ├─ beforeSingletonCreation ⑤ 添加beanName到正在实例化的bean的set集合
  • │ │ │ ├─ createBean
  • │ │ │ │ ├─ doCreateBean ⑥ 执行创建实例
  • │ │ │ │ │ ├─ createBeanInstance ⑦ 创建实例过程
  • │ │ │ │ │ │ ├─ determineConstructorsFromBeanPostProcessors ⑧ 如果构造函数有@Autowired
  • │ │ │ │ │ │ │ ├─ autowireConstructor ⑨ 执行构造器实例过程不会走到步骤 ⑪
  • │ │ │ │ │ │ │ │ └─ getBean 返回 ① 实例化入口
  • │ │ │ │ │ ├─ applyMergedBeanDefinitionPostProcessors ⑩ 注解的搜集和装配过程
  • │ │ │ │ │ ├─ earlySingletonExposure ⑪ 暴露还没有完全实例化完成的 bean
  • │ │ │ │ │ ├─ addSingletonFactory ⑫ 将提前暴露的 bean,放到三级缓存
  • │ │ │ │ │ ├─ populateBean ⑬ 注依赖注入的核心过程
  • │ │ │ │ │ │ ├─ inject
  • │ │ │ │ │ │ │ ├─ AutowiredFieldElement.inject ⑭ 有@Autowired注解域的注入
  • │ │ │ │ │ │ │ │ └─ getBean 返回①实例化入口
  • │ │ │ │ │ │ │ ├─ AutowiredMethodElement.inject ⑭ 有@Autowired注解方法的注入
  • │ │ │ │ │ │ │ │ └─ getBean 返回①实例化入口
  • │ │ │ │ │ ├─ initializeBean ⑮ bean 实例化和IOC依赖注入完以后的增强处理过程
  • │ │ │ │ │ └─ registerDisposableBeanIfNecessary ⑮ bean 的销毁过程
  • │ │ │ ├─ afterSingletonCreation ⑯ 实例化完成,将beanName从正在实例化的bean的set集合中删除
  • │ │ │ └─ addSingleton ⑰ 创建完成后,将完全实例化后的bean 放到一级缓存

# 循环依赖的步骤说明

CircularRefA 简称为 A,CircularRefB 简称为B,循环依赖步骤如下:

  • 1、A类首先从① 实例化入口进来,会一直执行到⑦ 创建实例后,执行⑫设置三级缓存
  • 2、A类 继续执行⑬ populateBean进行依赖注入,这里触发了 B类属性的 getBean操作,重新返回① 实例化入口
  • 3、B类一直执行到⑦ 创建实例后,执行⑫ 设置三级缓存
  • 4、B类继续执行⑬ populateBean进行依赖注入,这里触发了 A类属性的 getBean操作,重新返回① 实例化入口
  • 5、A类之前正在实例化,⑫ singletonsCurrentlyInCreation集合中有已经有这个 A类了,三级缓存里面也有了,所以这时候是从三级缓存中拿到的提前暴露的A实例,该实例还没有进行 B类属性的依赖注入的,B类属性为空。
  • 6、B类拿到了 A提前暴露的实例后,顺利的执行完⑬ 依赖注入,执行⑰创建完成后,将完全实例化后的bean⑰ 放到一级缓存,至此B类的实例化已经完全做完;
  • 7、由于B类的实例化是由 A类实例化中 B属性的依赖注入触发的 getBean操作进行的,现在 B已经实例化,所以 A类中 B属性就可以完成⑬ populateBean了,这时候 A类 B属性已经有值了;
  • 8、B类中的 A属性指向的就是 A类实例堆空间,所以这时候 B类中 A属性也会有值了;
  • 9、此时A类顺利的得到了B属性的注入值,也完成了自己的初始化流程,也会将将全实例化后的bean ⑰ 放到一级缓存
  • 10、循环依赖正式结束。

# 循环依赖的补充说明

如果是在createBeanInstance中,当前正在实例化的bean中有@Autowired注解的构造函数,触发getBean

org.springframework.beans.factory.support.BeanDefinitionValueResolver#resolveReference

public Object resolveValueIfNecessary(Object argName, @Nullable Object value) {
		// We must check each value to see whether it requires a runtime reference
		// to another bean to be resolved.
		if (value instanceof RuntimeBeanReference) {
			RuntimeBeanReference ref = (RuntimeBeanReference) value;
			return resolveReference(argName, ref);
		}
}

private Object resolveReference(Object argName, RuntimeBeanReference ref) {
		// 触发getBean 实例化
		bean = this.beanFactory.getParentBeanFactory().getBean(refName);
	}

如果是在populateBean中,@Autowired注解的构造函数依赖注入,并且注入参数是引用类型的话,最终也会触发 getBean进行实例化

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBeanInstance

		//寻找当前正在实例化的bean中有@Autowired注解的构造函数
		Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
		if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
				mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
			//如果ctors不为空,就说明构造函数上有@Autowired注解
			return autowireConstructor(beanName, mbd, ctors, args);
		}

org.springframework.beans.factory.support.DefaultListableBeanFactory


if (instanceCandidate instanceof Class) {
		//在这里真正拿到了构造函数中依赖的实例
		instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this);
			}

org.springframework.beans.factory.config.DependencyDescriptor

public Object resolveCandidate(String beanName, Class<?> requiredType, BeanFactory beanFactory)
			throws BeansException {
		// 将引用类型实例化
		return beanFactory.getBean(beanName);
	}

# 循环依赖的流程图

点击查看流程图

# Bean的销毁

首先我们来看一下⑯ Bean的销毁过程

进入 doCreateBean () 方法

类文件: org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory

try {
	... 
	
	//注册bean销毁时的类DisposableBeanAdapter
	registerDisposableBeanIfNecessary(beanName, bean, mbd);
}
catch (BeanDefinitionValidationException ex) {
	throw new BeanCreationException(
		mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
}

进入 registerDisposableBeanIfNecessary () 方法

类文件: org.springframework.beans.factory.support.AbstractBeanFactory

protected void registerDisposableBeanIfNecessary(String beanName, Object bean, RootBeanDefinition mbd) {
		AccessControlContext acc = (System.getSecurityManager() != null ? getAccessControlContext() : null);
		if (!mbd.isPrototype() && requiresDestruction(bean, mbd)) {
			// 如果是单例 bean
			if (mbd.isSingleton()) {
				// 对这个bean注册一个销毁的Adapter对象,对给定的bean执行销毁工作
				// 过滤了 DestructionAwareBeanPostProcessor 类型的接口
				registerDisposableBean(beanName,
						new DisposableBeanAdapter(bean, beanName, mbd, getBeanPostProcessors(), acc));
			}
			else {
				// 拿到bean的作用域
				Scope scope = this.scopes.get(mbd.getScope());
				if (scope == null) {
					throw new IllegalStateException("No Scope registered for scope name '" + mbd.getScope() + "'");
				}
				scope.registerDestructionCallback(beanName,
						new DisposableBeanAdapter(bean, beanName, mbd, getBeanPostProcessors(), acc));
			}
		}
	}

在 bean创建完成后就会对这个 bean注册一个销毁的 Adapter对象

销毁的核心类DisposableBeanAdapter就是负责bean销毁的类。在这个类中收集了该bean 是否实现了 DisposableBean接口,是否配置 destroy-method属性,如果有则 执行其destroy() 方法。

问题

Bean的销毁过程是什么时候触发的呢?

答案

  • 在Spring 中执行显式关闭上下文,关闭时候会执行实现DisposableBean接口类中的destroy() 方法,如:
ConfigurableApplicationContext context = new ClassPathXmlApplicationContext(new String[] {"spring.xml"});
// 以下两种显式关闭都会调用实现了DisposableBean接口类中的destroy() 方法
// 1、显式关闭上下文
context.close();
// 2、显式关闭上下文
context.registerShutdownHook();
  • 在Web环境中,当Tomcat关闭的时候就会调用到 servlet中的销毁方法,在这个方法中就会最终也会掉用到Spring中 DisposableBeanAdapter类的destroy()方法,该方法就会根据前面的收集进行调用。

servlet中的销毁方法

public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
    public ContextLoaderListener() {
    }

    public ContextLoaderListener(WebApplicationContext context) {
        super(context);
    }

    public void contextInitialized(ServletContextEvent event) {
        this.initWebApplicationContext(event.getServletContext());
    }
	//  servlet中的销毁方法
    public void contextDestroyed(ServletContextEvent event) {
       // 该方法最终会调用Spring的DisposableBeanAdapter类的destroy()
        this.closeWebApplicationContext(event.getServletContext());
        ContextCleanupListener.cleanupAttributes(event.getServletContext());
    }
}